package com.mf.all.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mf.all.entity.dto.TaskBo;
import com.mf.all.entity.req.publicReq.PublicUpdateStatusReq;
import com.mf.all.entity.req.search.TaskSearchReq;
import com.mf.all.service.ITaskNotificationService;
import com.mf.all.service.ITaskService;
import com.mf.all.emuns.ResultCodeEnum;
import com.mf.all.emuns.TaskStatusEnum;
import com.mf.all.exception.GraceException;
import com.mf.all.mapper.TaskMapper;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

/**
 * 任务表 服务实现类
 *
 * @author mf
 * @since 2025-03-08
 */
@Service
public class TaskServiceImpl extends ServiceImpl<TaskMapper, TaskBo> implements ITaskService {
    @Resource
    private ITaskNotificationService taskNotificationService;

    /**
     * 查询任务
     */
    @Override
    public Page<TaskBo> findPageList(TaskSearchReq taskSearchReq) {
        return baseMapper.selectPage(taskSearchReq.getPage(), getWrapper(taskSearchReq));
    }

    /**
     * 更新【进行中任务】指定日期任务通知
     */
    @Override
    public void updateTaskDayNotification(LocalDate date) {
        LambdaQueryWrapper<TaskBo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TaskBo::getStatus, TaskStatusEnum.IN_PROGRESS.getCode());
        List<TaskBo> taskBos = baseMapper.selectList(wrapper);
        taskNotificationService.setDayNotification(date, taskBos);
    }

    /**
     * 根据pid查询子任务
     */
    @Override
    public List<TaskBo> findByPidList(TaskSearchReq taskSearchReq) {
        return baseMapper.selectList(taskSearchReq.getPage(), getWrapper(taskSearchReq));
    }

    /**
     * 获取查询条件
     */
    private LambdaQueryWrapper<TaskBo> getWrapper(TaskSearchReq taskSearchReq) {
        final LambdaQueryWrapper<TaskBo> wrapper = new LambdaQueryWrapper<>();

        // 没有pid并且不是穿透查询
        if (taskSearchReq.getPid() != null && !taskSearchReq.getIsPenetrate()) {
            wrapper.eq(TaskBo::getPid, taskSearchReq.getPid());
        }
        if (StringUtils.isNotBlank(taskSearchReq.getKey())) {
            wrapper.like(TaskBo::getTaskName, taskSearchReq.getKey())
                    .or()
                    .like(TaskBo::getRemark, taskSearchReq.getKey());
        }
        if (taskSearchReq.getStatus() != null) {
            wrapper.eq(TaskBo::getStatus, taskSearchReq.getStatus());
        }

        if (taskSearchReq.getPrincipalId() != null) {
            wrapper.eq(TaskBo::getPrincipalId, taskSearchReq.getPrincipalId());
        }

        if (taskSearchReq.getExecutorId() != null) {
            wrapper.eq(TaskBo::getExecutorId, taskSearchReq.getExecutorId());
        }

        if (taskSearchReq.getPlanStart() != null && taskSearchReq.getPlanEnd() != null) {
            wrapper.between(TaskBo::getPlanStart, taskSearchReq.getPlanStart(), taskSearchReq.getPlanEnd());
        }

        if (taskSearchReq.getStartTime() != null && taskSearchReq.getEndTime() != null) {
            wrapper.between(TaskBo::getStartTime, taskSearchReq.getStartTime(), taskSearchReq.getEndTime());
        }

        wrapper.orderByDesc(TaskBo::getCreatedTime);
        return wrapper;
    }

    /**
     * 修改任务状态
     */
    @Override
    @Transactional
    public Integer updateStatusByIds(PublicUpdateStatusReq<Integer> statusReq) {
        final Integer status = statusReq.getStatus();
        final Integer id = statusReq.getId();

        String errorMessage = ResultCodeEnum.STATUS_UN_MODIFIABLE.getMessage();

        final TaskBo taskBo = findById(id);


        final int taskStatus = taskBo.getStatus().intValue();
        // 如果修改状态为【未开始】 只能是暂停状态才可以进行修改
        if (TaskStatusEnum.UN_START.getCode().equals(status)) {
            if (TaskStatusEnum.PAUSE.getCode().equals(taskStatus)) {
                baseMapper.updateBatchStatusByIds(List.of(id), status);
            } else {
                GraceException.display(errorMessage);
            }
        } else if (TaskStatusEnum.IN_PROGRESS.getCode().equals(status)) {
            // 如果是修改状态为【进行中】 只能是进行中、暂停状态才可以进行修改
            if (TaskStatusEnum.UN_START.getCode().equals(taskStatus)
                    || TaskStatusEnum.PAUSE.getCode().equals(taskStatus)) {
                baseMapper.updateBatchStatusByIds(List.of(id), status);
            } else {
                GraceException.display(errorMessage);
            }
        } else if (TaskStatusEnum.PAUSE.getCode().equals(status)) {
            // 如果是修改状态为【暂停】 只能是进行中、暂停状态才可以进行修改
            if (taskStatus < TaskStatusEnum.PAUSE.getCode()) {
                // 暂停需要把子任务都暂停掉
                updateStatusTaskById(id, status);
            } else {
                GraceException.display(errorMessage);

            }
        } else if (TaskStatusEnum.END.getCode().equals(status)
                || TaskStatusEnum.CANCELLED.getCode().equals(status)
                || TaskStatusEnum.CANCELLATION.getCode().equals(status)
        ) {
            // 如果是修改状态为【结束,取消,作废】 状态必须小于结束状态
            if (taskStatus < TaskStatusEnum.END.getCode()) {
                // 暂停需要把子任务都暂停掉
                updateStatusTaskById(id, status);
            } else {
                GraceException.display(errorMessage);
            }
        }
        taskBo.setStatus(status.byteValue());
        taskNotificationService.setTaskNotification(taskBo);
        return null;
    }

    /**
     * 作废任务
     * 递归把子任务全部作废
     */
    @Override
    public Integer cancellation(int id) {
        TaskBo taskBo = findById(id);
        if (taskBo.getStatus() > TaskStatusEnum.END.getCode()) {
            GraceException.display("当前任务不可作废");
        }
        return updateStatusTaskById(id, TaskStatusEnum.CANCELLATION.getCode());
    }

    /**
     * 根据id修改任务以及子任务状态
     */
    public Integer updateStatusTaskById(int id, int status) {
        // 获取所有子任务
        final List<TaskBo> taskBoList = findByPid(id);
        // 有子任务那么查询大于当前id的任务，然后在这些任务中把当前id关联的所有子任务全部查询出来
        if (!taskBoList.isEmpty()) {
            final LambdaQueryWrapper<TaskBo> wrapper = new LambdaQueryWrapper<>();
            // 大于当前id
            wrapper.gt(TaskBo::getId, id);
            final List<TaskBo> taskBoList1 = baseMapper.selectList(wrapper);
            // 递归查询出所有子任务id
            final List<Integer> updateIds = findByTaskBoListAndId(taskBoList1, id, new ArrayList<>());
            // 把当前id状态也修改过来
            updateIds.add(id);
            return baseMapper.updateBatchStatusByIds(updateIds, status);
        }
        return baseMapper.updateBatchStatusByIds(List.of(id), status);
    }

    /**
     * 删除任务 加个锁
     */
    @Override
    @Transactional
    public Integer deleteById(int id) {
        final TaskBo taskBo = findById(id);
        if (taskBo.getStatus().intValue() != TaskStatusEnum.UN_START.getCode()) {
            GraceException.display("只能删除未开始任务");
        }

        // 有pid父级子元素减一
        if (taskBo.getPid() != 0) {
            synchronizedUpdateSonCount(taskBo.getPid(), -1);
        }

        // 查询有没有子任务

        if (taskBo.getSonCount() > 0) {
            GraceException.display("任务下有子任务不可直接删除");
        }
        taskNotificationService.deleteByTaskId(id);
        return baseMapper.deleteById(id);
    }

    private synchronized void synchronizedUpdateSonCount(int id, Integer sonCount) {
        // 更新子任务数量
        baseMapper.updateSonCountById(id, sonCount);
    }

    /**
     * 根据任务集合和任务id查询任务id的所有子任务
     */
    private List<Integer> findByTaskBoListAndId(List<TaskBo> taskBoList, Integer id, List<Integer> deleteIds) {
        for (TaskBo taskBo : taskBoList) {
            // 当前任务是子任务
            if (taskBo.getPid().equals(id)) {
                deleteIds.add(taskBo.getId());
                findByTaskBoListAndId(taskBoList, taskBo.getId(), deleteIds);
            }
        }
        return deleteIds;
    }

    /**
     * 编辑任务
     */
    @Override
    public Integer updateTaskById(TaskBo taskBo) {
        // 验证当前任务计划时间是否合理
        verifyTaskTime(taskBo);
        // 验证当前任务状态是否合理
        verifyTaskStatus(taskBo.getId());
        baseMapper.updateById(taskBo);
        final TaskBo taskBo1 = findById(taskBo.getId());
        taskNotificationService.setTaskNotification(taskBo1);
        return null;
    }


    /**
     * 新增任务
     */
    @Override
    @Transactional
    public Integer saveTask(TaskBo taskBo) {
        // 验证当前任务计划时间是否合理
        verifyTaskTime(taskBo);
        synchronizedUpdateSonCount(taskBo.getPid(), 1);

        return baseMapper.insert(taskBo);
    }

    /**
     * 验证任务时间是否合理
     */
    private void verifyTaskTime(TaskBo taskBo) {
        if (taskBo.getPid() == null || taskBo.getPid() == 0) {
            return;
        }
        // 父任务
        final TaskBo PTaskBo = findById(taskBo.getPid());
        if (PTaskBo == null) {
            return;
        }
        // 当前任务计划开始时间不能早于父任务计划开始时间
        if (taskBo.getPlanStart().isBefore(PTaskBo.getPlanStart())) {
            GraceException.display("当前任务开始时间不能早于父任务开始时间");
        }
        // 当前任务计划结束时间不能晚于父任务计划结束时间
        if (taskBo.getPlanEnd().isAfter(PTaskBo.getPlanEnd())) {
            GraceException.display("当前任务结束时间不能晚于父任务结束时间");
        }
    }


    /**
     * 验证任务状态是否可操作
     */
    @Override
    public void verifyTaskStatus(int id) {
        final TaskBo taskBo1 = findById(id);
        if (taskBo1.getStatus() > TaskStatusEnum.PAUSE.getCode()) {
            GraceException.display("当前任务不可继续操作");
        }
    }

    /**
     * 根据id查询任务
     */
    private TaskBo findById(Integer id) {
        final LambdaQueryWrapper<TaskBo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TaskBo::getId, id);
        return baseMapper.selectOne(wrapper);
    }


    /**
     * 根据pid查询子任务
     */
    private List<TaskBo> findByPid(Integer pid) {
        final LambdaQueryWrapper<TaskBo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TaskBo::getPid, pid);
        return baseMapper.selectList(wrapper);
    }
}
